package com.diy.sigmund.diycommon.util.json;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Objects;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * json工具类，对jackson包的应用
 *
 * @author ylm-sigmund
 * @since 2020/11/21 13:16
 */
public class JacksonUtil {

    /**
     * 对象映射器
     */
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    /**
     * 日志
     */
    private static final Logger LOG = LoggerFactory.getLogger(JacksonUtil.class);
    /** 默认日期时间格式 */
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /** 默认日期格式 */
    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    /** 默认时间格式 */
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    static {
        // 序列化时格式化，按一定格式反序列化
        // "date":"2020-11-25 21:24:31"
        // date=Wed Nov 25 21:18:21 CST 2020
        // SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        // OBJECT_MAPPER.setDateFormat(dateFormat);
        // 全局忽略值为null的字段:指示仅包含具有非空值的属性的值。
        // @JsonInclude(JsonInclude.Include.NON_NULL) // 1、类级别；2、属性级别
        OBJECT_MAPPER.setSerializationInclusion(Include.NON_NULL);
        // 设置忽略多余属性:确定遇到未知属性（不映射到属性的属性，并且没有“任何设置器”或可以处理它的处理程序）是否应导致失败（通过抛出JsonMappingException ）的功能。此设置只有在尝试了所有其他未知属性的处理方法后才生效，并且属性保持未处理状态。
        // 默认情况下启用功能（意味着如果遇到未知属性将抛出JsonMappingException ）。
        OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 确定在没有找到类型的访问器时会发生什么的功能（并且没有注释表明它应该被序列化）。如果启用（默认），则会引发异常以指示这些为不可序列化类型；如果禁用，它们将被序列化为空对象，即没有任何属性。
        // 请注意，此功能仅对那些没有任何可识别注释（如@JsonSerialize ）的“空”bean 有效的空类型：具有注释的空类型不会导致抛出异常。
        // 功能默认启用。
        OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS,false);

        OBJECT_MAPPER.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        OBJECT_MAPPER.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);

        //LocalDateTime系列序列化和反序列化模块，继承自jsr310，我们在这里修改了日期格式
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class,new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addSerializer(LocalDate.class,new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        javaTimeModule.addSerializer(LocalTime.class,new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDateTime.class,new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDate.class,new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        javaTimeModule.addDeserializer(LocalTime.class,new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //Date序列化和反序列化
        javaTimeModule.addSerializer(Date.class, new JsonSerializer<Date>() {
            @Override
            public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                SimpleDateFormat formatter = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
                String formattedDate = formatter.format(date);
                jsonGenerator.writeString(formattedDate);
            }
        });
        javaTimeModule.addDeserializer(Date.class, new JsonDeserializer<Date>() {
            @Override
            public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
                SimpleDateFormat format = new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT);
                String date = jsonParser.getText();
                try {
                    return format.parse(date);
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        OBJECT_MAPPER.registerModule(javaTimeModule);
    }

    /**
     * 序列化对象为字符串
     * <p>
     * OBJECT_MAPPER.writerWithDefaultPrettyPrinter() 漂亮打印机
     *
     * @param value value
     * @return String
     */
    public static String toJson(Object value) {
        if (Objects.isNull(value)) {
            LOG.warn("toJson value 为空，此处转换为空字符串");
            return "";
        }
        String jsonString = "";
        try {
            jsonString = OBJECT_MAPPER.writeValueAsString(value);
        } catch (Exception exception) {
            LOG.error("toJson error, value={}, exception={}", value, exception);
        }
        return jsonString;
    }

    /**
     * 反序列化对象
     * <p>
     * 例如：JacksonUtil.toObject(mapJson, new TypeReference<Map<Integer, String>>() {});
     *
     * @param content       content
     * @param typeReference typeReference
     * @param <T>           <T>
     * @return 结果信息
     */
    public static <T> T toObject(String content, TypeReference<T> typeReference) {
        if (StringUtils.isBlank(content)) {
            LOG.warn("toObject content isBlank");
            return null;
        }
        T object = null;
        try {
            object = OBJECT_MAPPER.readValue(content, typeReference);
        } catch (Exception exception) {
            LOG.error("toObject error, content={}, exception={}", content, exception);
        }
        return object;
    }

    /**
     * 反序列化对象
     *
     * @param content   content
     * @param valueType Class
     * @param <T>       T
     * @return T
     */
    public static <T> T toObject(String content, Class<T> valueType) {
        if (StringUtils.isBlank(content)) {
            LOG.warn("toObject content isBlank");
            return null;
        }
        T object = null;
        try {
            object = OBJECT_MAPPER.readValue(content, valueType);
        } catch (Exception exception) {
            LOG.error("toObject error, content={}, exception={}", content, exception);
        }
        return object;
    }

}
